package cn.song.utils;

import cn.hutool.core.io.resource.ResourceUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Markdown 转 SQL
 *
 * @author SongRenShuo
 * @date 2025/02/12
 */
@Slf4j
public class MarkdownToSql {

    /**
     * 主方法，程序入口
     *
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        // 定义Markdown文件路径
        String markdownPath = "static/markdown/用户角色权限.md";
        try {
            // 解析Markdown文件并获取表信息
            List<Map<String, Object>> tablesInfo = parseMarkdownFile(markdownPath);
            // 根据表信息生成SQL文件
            getSql(tablesInfo, "rbac.sql");
        } catch (IOException e) {
            // 记录异常信息
            log.error("异常:", e);
        }
    }

    /**
     * 解析Markdown文件，提取表信息
     *
     * @param filePath Markdown文件路径
     * @return 表信息列表
     * @throws IOException 文件读取异常
     */
    private static List<Map<String, Object>> parseMarkdownFile(String filePath) throws IOException {
        // 初始化表信息列表
        List<Map<String, Object>> tablesInfo = new ArrayList<>();
        // 当前表信息
        Map<String, Object> currentTable = null;
        // 表头信息
        List<String> headers = null;
        // 获取文件读取器
        BufferedReader reader = ResourceUtil.getUtf8Reader(filePath);

        // 逐行读取文件内容
        String line;
        while ((line = reader.readLine()) != null) {
            // 去除行首尾空格
            line = line.trim();
            if (StrUtil.isBlank(line)) {
                continue;
            }
            // 新表开始
            if (line.startsWith("###")) {
                // 如果当前表不为空，检查字段名是否重复并添加到表信息列表
                if (currentTable != null) {
                    checkWhetherFieldNamesAreDuplicated(currentTable);
                    tablesInfo.add(currentTable);
                }

                // 提取表名和注释
                currentTable = extractTableNameAndComment(line);

                // 标题
            } else if (line.contains("列名")) {
                // 如果表头为空，提取表头信息
                if (headers == null) {
                    headers = extractHeaders(line);
                }
                // 表格行
            } else if (line.startsWith("|") && line.contains("|") && !line.contains("--") && headers != null) {
                // 添加列信息到当前表
                addColumn(currentTable, line, headers);
            }
        }
        // 如果表头为空，抛出异常
        if (headers == null) {
            throw new RuntimeException("没有找到表头");
        }
        // 如果当前表不为空，添加到表信息列表
        if (currentTable != null) {
            tablesInfo.add(currentTable);
        }
        // 关闭文件读取器
        reader.close();
        return tablesInfo;
    }

    /**
     * 检查字段名是否重复
     *
     * @param currentTable 当前表信息
     */
    private static void checkWhetherFieldNamesAreDuplicated(Map<String, Object> currentTable) {
        // 获取列信息列表
        List<Map<String, Object>> columns = (List<Map<String, Object>>) currentTable.get("columns");
        // 按字段名分组
        Map<Object, List<Map<String, Object>>> collect = columns.parallelStream()
                .filter(column -> column.get("name") != null)
                .collect(Collectors.groupingBy(column -> column.get("name")));
        // 检查是否有重复字段名
        if (!collect.isEmpty()) {
            collect.forEach((key, value) -> {
                if (value.size() > 1) {
                    // 获取表名
                    String table = currentTable.getOrDefault("tableName", "").toString();
                    // 记录错误信息并抛出异常
                    log.error("字段名重复：{} 数据表名：{}  数据表信息：{}", key, table, JSONUtil.toJsonStr(currentTable));
                    throw new RuntimeException("字段名重复：" + key + " 数据表：" + table);
                }
            });
        }

    }

    /**
     * 提取表名和注释
     *
     * @param line 包含表名和注释的行
     * @return 表信息
     */
    private static Map<String, Object> extractTableNameAndComment(String line) {
        // 初始化表信息
        Map<String, Object> tableInfo = new HashMap<>();
        // 提取表名和注释
        String[] split01 = line.replace("###", "").trim().split("\\.");
        String value = split01.length > 1 ? split01[1] : split01[0];
        String[] split02 = value.split("\\(");
        // 设置表名
        tableInfo.put("tableName", split02[0]);
        // 设置表注释
        tableInfo.put("tableComment", split02[1].replace(")", ""));
        // 初始化列信息列表
        tableInfo.put("columns", new ArrayList<>());
        return tableInfo;
    }

    /**
     * 生成创建表的SQL语句
     *
     * @param tableInfo 表信息
     * @return 创建表的SQL语句
     */
    private static String generateCreateTableSQL(Map<String, Object> tableInfo) {
        // 初始化SQL语句构建器
        StringBuilder sqlBuilder = new StringBuilder();
        // 获取表名
        String tableName = (String) tableInfo.get("tableName");
        // 获取表注释
        String tableComment = (String) tableInfo.get("tableComment");
        // 获取列信息列表
        List<Map<String, Object>> columns = (List<Map<String, Object>>) tableInfo.get("columns");

        // 检查是否已经有id字段，如果没有则添加
        boolean hasIdColumn = false;
        for (Map<String, Object> column : columns) {
            if ("id".equalsIgnoreCase((String) column.get("name"))) {
                hasIdColumn = true;
                break;
            }
        }
        if (!hasIdColumn) {
            // 创建id列信息
            Map<String, Object> idColumn = new HashMap<>();
            idColumn.put("name", "id");
            idColumn.put("primaryKey", true);
            idColumn.put("autoIncrement", true);
            idColumn.put("description", "主键");
            idColumn.put("type", "int(11)");
            idColumn.put("required", false);
            // 添加到最前面
            columns.add(0, idColumn);
        }

        // 开始构建SQL语句
        sqlBuilder.append("CREATE TABLE ").append(tableName).append(" (\n");
        // 遍历列信息列表
        for (Map<String, Object> column : columns) {
            // 添加列名和数据类型
            sqlBuilder.append("  `").append(column.get("name")).append("` ")
                    .append(column.get("type")).append(" ");
            // 添加是否为空约束
            if (!(Boolean) column.get("required")) {
                sqlBuilder.append("NOT NULL ");
            }
            // 添加主键约束
            if (column.containsKey("primaryKey") && (Boolean) column.get("primaryKey")) {
                sqlBuilder.append("PRIMARY KEY ");
            }
            // 添加自增约束
            if (column.containsKey("autoIncrement") && (Boolean) column.get("autoIncrement")) {
                sqlBuilder.append("AUTO_INCREMENT ");
            }
            // 添加默认值处理逻辑
            if (column.containsKey("defaultValue") && StrUtil.isNotBlank(column.getOrDefault("defaultValue", "").toString())) {
                sqlBuilder.append("DEFAULT '").append(column.get("defaultValue")).append("' ");
            }
            // 添加备注
            if (column.containsKey("description") && !((String) column.get("description")).isEmpty()) {
                sqlBuilder.append("COMMENT '").append(column.get("description")).append("' ");
            }
            // 添加逗号和换行符
            sqlBuilder.append(",\n");
        }
        // 删除最后一个逗号和换行符
        sqlBuilder.setLength(sqlBuilder.length() - 2);
        // 添加唯一约束
        for (Map<String, Object> column : columns) {
            if ((Boolean) column.getOrDefault("unique",false)) {
                sqlBuilder.append(",\n  UNIQUE (`").append(column.get("name")).append("`)");
            }
        }
        // 添加表注释并结束SQL语句
        sqlBuilder.append("\n) COMMENT='").append(tableComment).append("';");

        return sqlBuilder.toString();
    }

    /**
     * 提取表头信息
     *
     * @param headerLine 包含表头的行
     * @return 表头信息列表
     */
    private static List<String> extractHeaders(String headerLine) {
        // 初始化表头信息列表
        List<String> headers = new ArrayList<>();
        // 分割表头行
        String[] parts = headerLine.split("\\|");
        // 忽略第一个元素
        for (int i = 1; i < parts.length; i++) {
            // 添加表头信息
            headers.add(parts[i].trim());
        }
        return headers;
    }

    /**
     * 添加列信息到表
     *
     * @param tableInfo 表信息
     * @param line      包含列信息的行
     * @param headers   表头信息列表
     */
    private static void addColumn(Map<String, Object> tableInfo, String line, List<String> headers) {
        // 分割列信息行
        String[] values = line.split("\\|");
        // 删除第一个元素
        values = Arrays.copyOfRange(values, 1, values.length);
        // 初始化列信息
        Map<String, Object> column = new HashMap<>();

        // 遍历表头信息列表
        for (int i = 0; i < headers.size(); i++) {
            // 获取列值
            String value = StrUtil.trim(values[i]);
            // 直接使用索引定位到正确的列
            switch (headers.get(i)) {
                case "列名":
                    // 设置列名
                    column.put("name", value);
                    // 如果列名为id，设置为主键和自增
                    if ("id".equalsIgnoreCase(value)) {
                        column.put("primaryKey", true);
                        column.put("autoIncrement", true);
                    }
                    break;
                case "数据类型":
                    // 检查是否包含长度信息
                    boolean isExistLength = value.contains("(") && value.contains(")");
                    // 添加默认长度处理逻辑
                    switch (value.toLowerCase()) {
                        case "varchar":
                            column.put("type", isExistLength ? value : value + "(255)");
                            break;
                        case "int":
                            column.put("type", isExistLength ? value : value + "(11)");
                            break;
                        case "double":
                            column.put("type", isExistLength ? value : value + "(11,4)");
                            break;
                        default:
                            column.put("type", value);
                            break;
                    }
                    break;
                case "是否为空":
                    // 转换为布尔值
                    value = value.replace("否", "false").replace("是", "true");
                    column.put("required", Boolean.parseBoolean(value));
                    break;
                case "主键":
                    // 转换为布尔值
                    value = value.replace("否", "false").replace("是", "true");
                    column.put("primaryKey", Boolean.parseBoolean(value));
                    break;
                case "自增":
                    // 转换为布尔值
                    value = value.replace("否", "false").replace("是", "true");
                    column.put("autoIncrement", Boolean.parseBoolean(value));
                    break;
                case "默认值":
                    // 设置默认值
                    column.put("defaultValue", value);
                    break;
                case "备注":
                    // 设置备注
                    column.put("description", value);
                    // 如果备注包含唯一，设置唯一约束
                    if (value.contains("唯一")) {
                        column.put("unique", true);
                    }
                    break;
                default:
                    // 抛出异常
                    throw new IllegalArgumentException("未知的表头：" + headers.get(i));
            }

        }

        // 根据字段说明判断是否唯一，唯一时长度为188
        String type = column.get("type").toString();
        if ((Boolean) column.getOrDefault("unique",false) && type.contains("varchar") && type.contains("255")) {
            column.put("type", type.replace("255", "188"));
        }

        // 添加列信息到表信息列表
        ((List<Map<String, Object>>) tableInfo.get("columns")).add(column);
    }

    /**
     * 根据表信息生成SQL文件
     *
     * @param tablesInfo 表信息列表
     * @param sqlFileName   SQL文件名
     */
    public static void getSql(List<Map<String, Object>> tablesInfo, String sqlFileName) {

        try (BufferedWriter writer = new BufferedWriter(new FileWriter("sql/"+sqlFileName))) {
            // 记录当前条数
            int i = 0;
            // 遍历表信息列表
            for (Map<String, Object> tableInfo : tablesInfo) {
                // 生成创建表的SQL语句并写入文件
                writer.write(generateCreateTableSQL(tableInfo));
                writer.newLine();
                i++;
                // 打印当前条数
                System.out.println("当前条数：" + i);
            }
            // 打印总条数
            System.out.println("总条数：" + i);
        } catch (Exception e) {
            // 记录异常信息
            log.error("异常:", e);
        }
    }
}
